package com.redxiii.spike.engine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Daniel Filgueiras
 * @since 19/06/2012
 */
public class CoreTimerPool implements Serializable {

	private static final Logger logger = LoggerFactory.getLogger(CoreTimerPool.class);
	private static final long serialVersionUID = 1L;
	private static final CoreTimerPool instance;
	private static final AtomicBoolean timerAlive;
	
	private ThreadLocal<List<TimerTask>> timerTaskPool;
	private ThreadLocal<Timer> threadLocalTimer;
	
	static {
		instance = new CoreTimerPool();
		timerAlive = new AtomicBoolean(true);
	}
	
	class ThreadWatchDog extends TimerTask {

		private Thread parent;
		private Timer ownerTimer;
		
		public ThreadWatchDog(Thread parent, Timer owner) {
			logger.debug("Starting watch-dog for Thread {}", parent.getName());
			this.parent = parent;
			this.ownerTimer = owner;
		}


		@Override
		public void run() {
			logger.trace("Inspecting parent thread...");
			if (parent.isAlive() && timerAlive.get())
				return;
			
			ownerTimer.cancel();
			
			logger.debug("Parent thread is dead. Canceling all timers...");
			List<TimerTask> timerTasks = timerTaskPool.get();
			if (timerTasks == null)
				return;
			
			for (TimerTask task : timerTasks) {
				logger.debug("Canceling timer: '{}'", task);
				task.cancel();
			}
			
		}
	}
	
	public static CoreTimerPool getInstance() {
		return instance;
	}
	
	private CoreTimerPool(){
		this.timerTaskPool = new InheritableThreadLocal<List<TimerTask>>();
		this.threadLocalTimer = new InheritableThreadLocal<Timer>();
	}
	
	private Timer getTimer() {

		synchronized (threadLocalTimer) {
			Timer timer = threadLocalTimer.get();
			if (timer == null) {
				timer = new Timer(Thread.currentThread().getName() + ".Timer");
				threadLocalTimer.set(timer);
			}
			
			return timer;
		}
	}
	
	public void submitTask(TimerTask task, long delay, long period) {
		
		Timer timer = getTimer();
		
		List<TimerTask> timerTasks = timerTaskPool.get();
		if (timerTasks == null) {
			timerTasks = new ArrayList<TimerTask>();
			
			timerTaskPool.set(timerTasks);
			
			ThreadWatchDog dog = new ThreadWatchDog(Thread.currentThread(), timer);
			timer.schedule(dog, 1000 * 5, 1000 * 30);
		}
		
		timer.schedule(task, delay, period);
		
		return;
	}
	
	public void shutdown() {
		timerAlive.set(false);
	}
}

